/**
 * 第i行指示了第Ai行的答案，显然具有图的特征
 * 且每一行显然只有一个出度，所以是一个内向基环树的森林
 * 对每一个树首先找出环，然后对5种可能的答案各转一圈看是否满足即可
 * 注意链不影响答案，因为无论环上的合法点是谁，总可以选择一个合适的起点使得其走到环上
 */
#include <bits/stdc++.h>
using namespace std;

using llt = long long;
using vi = vector<int>;

llt const MOD = 998244353LL;

int N;
vector<string> S;
vector<vi> G;
vector<vi> Ig;
vi Flag;
llt Ans;

bool isOK(const vi & circle, int start, int ch){
	int n = circle.size() - 1;

	int cur = ch;
	for(int i=start;i<n;++i){
		auto index = circle[i];
        char cc = S[index][cur];
		cur = cc - 'A';
	}

	return cur == ch;
}

bool proc(int idx){
	vi vec; vec.reserve(N + 1);

	Flag[idx] = 1;
	int now = idx;
	vec.emplace_back(now);
	while(1){
        assert(G[now].size() == 1);
        vec.emplace_back(now = G[now][0]);
		if(Flag[now] == 1) break;
		Flag[now] = 1;
	}
 
    int k = vec.size() - 2;
	while(k >= 0 and vec[k] != now) k -= 1;
	// assert(k >= 0 and vec[k] == now);
	if(k < 0) return true;

	llt ans = 0;
    for(int i=0;i<5;++i){
		if(isOK(vec, k, i)) ++ans;
	}

    if(0 == ans) return false;
	Ans = Ans * ans % MOD;
	return true;
}

llt proc(){	
    Ans = 1;
    Flag.assign(N + 1, 0);
	for(int i=1;i<=N;++i){
		if(0 == Flag[i]){
			if(not proc(i)) return 0;
		}
	}    
    return Ans;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(0);
    int nofkase = 1;
    // cin >> nofkase;
    while(nofkase--){
        cin >> N;
		G.assign(N + 1, {});
		Ig.assign(N + 1, {});
		S.assign(N + 1, {});
		for(int a,i=1;i<=N;++i){
            cin >> a >> S[i];
			G[i].emplace_back(a);
			Ig[a].emplace_back(i);
		}
		cout << proc() << "\n";
    }
    return 0;
}